//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "ddk.h"

#include "CWin32Socket.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)
#include "_CWin32Socket.cpp"

#include "_w32usnetabrg.cpp"
#include "_w32usnetuuid.cpp"

//extern "C" int g_errno;

UInt32 CWin32Socket::AddRef()
{
    return (UInt32)InterlockedIncrement(&m_cRef);
}

UInt32 CWin32Socket::Release()
{
    UInt32 Refs;
    Refs = (UInt32)InterlockedDecrement(&m_cRef);

    if (Refs == 0) {
        delete this;
    }

    return Refs;
}

CWin32Socket::~CWin32Socket()
{
    WIN32_closesocket(m_Socket);
}

PInterface CWin32Socket::Probe(
        /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (IInterface*)this;
    }
    else if (riid == EIID_ISocket) {
        return (ISocket *)this;
    }
    else if(riid == EIID_ISocketEx) {
        return (ISocketEx *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (IInterface *)&ECLSID_CSocket;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (IInterface *) &EIID_ISocket;
    }

    return NULL;
}

ECode CWin32Socket::Accept(
    /* [out] */ ISocket ** ppIncomingConnection,
    /* [out] */ SocketAddr* pAddress,
    /* [out] */ Int32 * pError)
{
    CWin32Socket *pSocket = NULL;
    win32_sockaddr sa;
    win32_socket so;
    Int32 ret = 0;
    Int32 nameLen = sizeof(win32_sockaddr);

    assert(NULL != pAddress);
    assert(NULL != pError);
    assert(NULL != ppIncomingConnection);

    memset(&sa, 0, nameLen);
    ret = WIN32_accept(m_Socket, &sa, &nameLen, &so);
    if (0 != ret) {
        *pError = ret;
        memset(pAddress, 0, sizeof(SocketAddr));
        *ppIncomingConnection = (ISocket *)NULL;
        return S_FALSE;
    }

    pSocket = new CWin32Socket(so);
    if (NULL == pSocket) {
        return E_NOT_ENOUGH_MEMORY;
    }

    pSocket->AddRef();
    memcpy(pAddress, &sa, sizeof(SocketAddr));
    *ppIncomingConnection = (ISocket *)pSocket;
    return NOERROR;
}

ECode CWin32Socket::Bind(
    /* [in] */ const SocketAddr & address,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    struct win32_sockaddr sa;
    Int32 nameLen = sizeof(struct win32_sockaddr);

    assert(NULL != pError);
    
    memset(&sa, 0, nameLen);
    memcpy(&sa, &address, sizeof(SocketAddr));

    ret = WIN32_bind(m_Socket, &sa, nameLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    return NOERROR;
}

ECode CWin32Socket::Connect(
    /* [in] */ const SocketAddr & address,
    /* [out] */ Int32 * pError)
{
    // TODO: Add your code here
    struct win32_sockaddr sa;;
    Int32 ret = 0;
    Int32 nameLen = sizeof(struct win32_sockaddr);

    memset(&sa, 0, nameLen);
    memcpy(&sa, &address, sizeof(SocketAddr));

    ret = WIN32_connect(m_Socket, &sa, nameLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }
    return NOERROR;
}

ECode CWin32Socket::GetPeerName(
    /* [out] */ SocketAddr* pAddress,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 nameLen = 0;
    win32_sockaddr sa;

    assert(NULL != pAddress);
    assert(NULL != pError);

    nameLen = sizeof(win32_sockaddr);
    memset(&sa, 0, nameLen);

    ret = WIN32_getpeername(m_Socket, &sa, &nameLen);
    if (0 != ret) {
        *pError = ret;
        memset(pAddress, 0, sizeof(SocketAddr));
        return S_FALSE;
    }
    
    memcpy(pAddress, &sa, sizeof(SocketAddr));
    return NOERROR;
}

ECode CWin32Socket::GetSockName(
    /* [out] */ SocketAddr* pAddress,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 nameLen = 0;
    win32_sockaddr sa;

    assert(NULL != pAddress);
    assert(NULL != pError);

    nameLen = sizeof(win32_sockaddr);
    memset(&sa, 0, nameLen);

    ret = WIN32_getsockname(m_Socket, &sa, &nameLen);
    if (0 != ret) {
        *pError = ret;
        memset(pAddress, 0, sizeof(SocketAddr));
        return S_FALSE;
    }

    memcpy(pAddress, &sa, sizeof(SocketAddr));
    return NOERROR;

}

ECode CWin32Socket::Listen(
    /* [in] */ Int32 backLog,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;

    assert(NULL != pError);

    ret = WIN32_listen(m_Socket, backLog);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    return NOERROR;
}

ECode CWin32Socket::Recv(
    /* [in] */ Flags32 flags,
    /* [in] */ Int32 bytesToRecv,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 recvLen = 0;

    assert(NULL != pError);
    assert(NULL != pBuffer);

    ret = WIN32_recv(m_Socket, 
                     (char *)(pBuffer->GetPayload()),
                     bytesToRecv,
                     flags,
                     &recvLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    pBuffer->SetUsed(recvLen);
    return NOERROR;
}

ECode CWin32Socket::RecvFrom(
    /* [in] */ Flags32 flags,
    /* [in] */ Int32 bytesToRecv,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ SocketAddr* pSourceAddr,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 recvLen = 0;
    Int32 addrLen = 0;
    win32_sockaddr sa;

    assert(NULL != pBuffer);
    assert(NULL != pSourceAddr);
    assert(NULL != pError);
    
    addrLen = sizeof(struct win32_sockaddr);
    memset(&sa, 0, addrLen);

    ret = WIN32_recvfrom(m_Socket,
                         (char *)(pBuffer->GetPayload()),
                         bytesToRecv,
                         flags, 
                         &sa,
                         &addrLen,
                         &recvLen);
    if (0 != ret) {
        *pError = ret;

        return S_FALSE;
    }

    pBuffer->SetUsed(recvLen);
    memcpy(pSourceAddr, &sa, sizeof(SocketAddr));

    return NOERROR;
}

ECode CWin32Socket::Send(
    /* [in] */ Flags32 flags,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesSent,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 sendLen = 0;

    assert(NULL != pBytesSent);
    assert(NULL != pError);

    ret = WIN32_send(m_Socket, 
                     (const char *)(buffer.GetPayload()), 
                     buffer.GetUsed(),
                     flags,
                     &sendLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    *pBytesSent = sendLen;
    return NOERROR;
}

ECode CWin32Socket::SendTo(
    /* [in] */ Flags32 flags,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesSent,
    /* [in] */ const SocketAddr & targetAddr,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 sendLen = 0;
    win32_sockaddr sto;

    assert(NULL != pBytesSent);
    assert(NULL != pError);

    memset(&sto, 0, sizeof(struct win32_sockaddr));
    memcpy(&sto, &targetAddr, sizeof(SocketAddr));
    ret = WIN32_sendto(m_Socket, 
                       (char *)(buffer.GetPayload()),
                       buffer.GetUsed(),
                       flags,
                       &sto, 
                       sizeof(win32_sockaddr),
                       &sendLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    *pBytesSent = sendLen;
    return NOERROR;
}

ECode CWin32Socket::Shutdown(
    /* [in] */ Int32 how,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;

    assert(NULL != pError);

    ret = WIN32_shutdown(m_Socket, how);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    return NOERROR;
}

ECode CWin32Socket::EventSelect(
    /* [in] */ IEvent * pEvent,
    /* [in] */ Int32 eventMask)
{
    // TODO: Add your code here
    assert(0);
    return E_NOT_IMPLEMENTED;
}

ECode CWin32Socket::SetSockOpt(
    /* [in] */ Int32 level,
    /* [in] */ Int32 optName,
    /* [in] */ const MemoryBuf & optValue,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Int32 optLen = optValue.GetUsed();

    assert(NULL != pError);
    assert(optLen > 0);

    ret = WIN32_setsockopt(m_Socket, level, optName, (char *)(optValue.GetPayload()), optLen);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    return NOERROR;
}

ECode CWin32Socket::GetSockOpt(
    /* [in] */ Int32 level,
    /* [in] */ Int32 optName,
    /* [in] */ Int32 optSize,
    /* [out] */ MemoryBuf * pOptValue,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    char *pBuf = NULL;

    assert(NULL != pOptValue);
    assert(NULL != pError);
    assert(pOptValue->GetCapacity() >= optSize);

    pBuf = (char *)(pOptValue->GetPayload());
    ret = WIN32_getsockopt(m_Socket, level, optName, pBuf, &optSize);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }
    return NOERROR;
}

ECode CWin32Socket::Ioctl(
    /* [in] */ Int32 command,
    /* [in] */ const MemoryBuf & inDataBuf,
    /* [out] */ MemoryBuf * pOutDataBuf,
    /* [out] */ Int32 * pError)
{
    Int32 ret = 0;
    Byte *pBuf = NULL;

    assert(NULL != pOutDataBuf);
    assert(NULL != pError);

    pBuf = (Byte *)malloc(inDataBuf.GetUsed());
    if (NULL == pBuf) {
        return E_OUT_OF_MEMORY;
    }
    memcpy(pBuf, inDataBuf.GetPayload(), inDataBuf.GetUsed());

    ret = WIN32_ioctlsocket(m_Socket, command, (unsigned long *)(pBuf));
    if (0 != ret) {
        *pError = ret;
        free(pBuf);
        return S_FALSE;
    }

    pOutDataBuf->Copy(pBuf, inDataBuf.GetUsed());
    free(pBuf);
    return NOERROR;
}

